/**
 * @author  Thomas Lehmann
 * @file    DefaultScanner.js
 * @brief   Keywords and scanner for C++ and similar languages.
 *
 * Copyright (c) 2013 Thomas Lehmann
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies
 * or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
var log;
var Class;
var StringTools;
var KeywordFactory;
var CommentFactory;

if (typeof require == 'function') {
    var ClassTools = require("./ClassTools.js");
    log            = ClassTools.log;
    Class          = ClassTools.Class;
    StringTools    = require("./StringTools.js").StringTools;
    KeywordFactory = require("./Keywords.js").KeywordFactory;
    CommentFactory = require("./Comments.js").CommentFactory;
}

var DefaultScanner = Class({
    initialize: function(addToken) {
        log("Creating instance of DefaultScanner");
        this.tools       = new StringTools();
        this.keywords    = KeywordFactory.createInstance("default");
        this.comments    = CommentFactory.createInstance("default");
        this.addToken    = addToken;
        this.style       = "default"
        this.lineNumbers = false;
    },

    scannerFunctions: function() {
        return new Array(this.scanLineComment,
                         this.scanCodeViewDirectives,
                         this.scanBlockComment,
                         this.scanDoubleQuotedString,
                         this.scanSingleQuotedString,
                         this.scanNumber,
                         this.scanBlanks,
                         this.scanBrackets,
                         this.scanIdentifier,
                         this.scanNewLine,
                         this.scanSpecialCharacter);
    },

    scanLineComment: function(text, index) {
        if (this.tools.startsWith(text, index, this.comments.lineComment)) {
            token = this.comments.lineComment;
            index += this.comments.lineComment.length;

            while (index < text.length && text[index] != "\n") {
                token += text[index];
                ++index;
            }

            this.addToken("line-comment", token);
            if (text[index-1] == "\n") {
                --index;
            }
        }
        return index;
    },

    scanCodeViewDirectives: function(text, index) {
        if (index+1 < text.length && text[index] == '@' && text[index+1] == '@') {
            token  = "";
            index += 2;

            while (index < text.length && text[index] != "\n") {
                token += text[index];
                ++index;
            }
            ++index;
            this.processCodeViewDirectives(token);
        }
        return index;
    },

    processCodeViewDirectives: function(token) {
        var directives = token.split("|");
        for (var index = 0; index < directives.length; ++index) {
            var oneDirective = directives[index].split("=")
            var name         = oneDirective[0]
            var value        = oneDirective[1]
            if (name == "language") {
                newKeywords = KeywordFactory.createInstance(value);
                if (newKeywords != undefined) {
                    this.keywords = newKeywords;
                } else {
                    alert("no keywords defined for language: " + value);
                }

                newComments = CommentFactory.createInstance(value);
                if (newComments != undefined) {
                    this.comments = newComments;
                } else {
                    alert("No comments defined for language: " + value);
                }
            } else if (name == "style") {
                this.style = value;
            } else if (name == "line-numbers") {
                this.lineNumbers = (value == "yes");
            }
        }
    },

    scanBlockComment: function(text, index) {
        if (this.tools.startsWith(text, index, this.comments.blockCommentStart)) {
            token = this.comments.blockCommentStart;
            index += this.comments.blockCommentStart.length;

            while (index < text.length &&
                   !this.tools.startsWith(text, index, this.comments.blockCommentEnd)) {
                token += text[index];
                ++index;
            }
            token += this.comments.blockCommentEnd;
            index += this.comments.blockCommentEnd.length;
            this.addToken("block-comment", token.replace(/\n/g, "<br/>"));
        }
        return index;
    },

    scanNewLine: function(text, index) {
        if (text[index] == '\n') {
            this.addToken("new-line", "<br/>");
            ++index;
        }
        return index;
    },

    scanBlanks: function(text, index) {
        if (text[index] == ' ') {
            var token = "";
            while (text[index] == ' ') {
                token += ' ';
                ++index;
            }
            this.addToken("blanks", token);
        }
        return index;
    },

    scanIdentifier: function(text, index) {
        if (this.tools.isAlpha(text[index]) || text[index] == '_') {
            var token = "" + text[index];
            ++index;
            while (this.tools.isAlpha(text[index]) ||
                   text[index] == '_' || this.tools.isDigit(text[index])) {
                token += text[index];
                ++index;
            }

            if (this.keywords.isKeyword(token)) {
                this.addToken("keyword", token);
            } else {
                this.addToken("identifier", token);
            }
        }
        return index;
    },

    scanSpecialCharacter: function(text, index) {
        var character = text[index];
        if (this.tools.isSpecialCharacter(character)) {
            if ("=<>+-".indexOf(character) >= 0 && (index+1) < text.length &&
                "=<>+-".indexOf(text[index+1]) >= 0) {
                this.addToken("operator", "" + character + text[index+1]);
                index += 2;
            } else if (":".indexOf(character) >= 0 && (index+1) < text.length &&
                       ":".indexOf(text[index+1]) >= 0) {
                this.addToken("scope", "" + character + text[index+1]);
                index += 2;
            } else {
                this.addToken("special-character", "" + character);
                ++index;
            }
        }
        return index;
    },

    scanBrackets: function(text, index) {
        if (this.tools.isBracket(text[index])) {
            this.addToken("bracket", "" + text[index]);
            ++index;
        }
        return index;
    },

    scanDoubleQuotedString: function(text, index) {
        if (text[index] == "\"") {
            token = "\""
            ++index;
            while (index < text.length && (text[index-1] == "\\" || text[index] != '"')) {
                token += text[index];
                ++index;
            }
            token += "\"";
            ++index;
            this.addToken("double-quoted-string", token);
        }
        return index;
    },

    scanSingleQuotedString: function(text, index) {
        if (text[index] == "'") {
            token = "'"
            ++index;
            while (index < text.length && (text[index-1] == "\\" || text[index] != '\'')) {
                token += text[index];
                ++index;
            }
            token += "'";
            ++index;
            this.addToken("single-quoted-string", token);
        }
        return index;
    },

    scanNumber: function(text, index) {
        if (this.tools.isDigit(text[index]) || text[index] == '.') {
            oldIndex = index;
            token = "" + text[index];
            ++index;
            while(this.tools.isDigit(text[index]) || text[index] == '.') {
                token += text[index];
                ++index;
            }
            if (token != ".") {
                this.addToken("number", token);
            } else {
                index = oldIndex;
            }
        }
        return index;
    }
});

if (typeof(exports) != 'undefined') {
    exports.DefaultScanner = DefaultScanner;
}
